/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
 * Description: Hmfs-tools resize load sit
 */

#include "load_sit.h"

#include <clocale>
#include <cstdbool>
#include <ctime>

#include "device_manager.h"
#include "hmfs_common.h"
#include "node.h"
#include "resize_load.h"

namespace OHOS {
namespace Hmfs {
LoadSit::LoadSit(ResizeLoad *loadPtr) : load_(loadPtr)
{
    config_ = load_->GetConfig();
    superBlockInfo_ = load_->GetSuperBlockInfo();
}

bool LoadSit::IsSyncDataRecord()
{
    return !IsCheckpointFlags(superBlockInfo_->checkPointData, CP_FLAG_UMOUNT) ||
        config_->zonedModel == HMFS_ZONED_HM;
}

int32_t LoadSit::InitializeSitInfo(SitInfo *sitInfo, uint32_t mainSegments)
{
    HMFS_CHECK_ONLY_EXPER(sitInfo != nullptr, return EXIT_ERR_CODE);
    sitInfo->segEntries = static_cast<SegmentEntry *>(calloc(mainSegments * sizeof(SegmentEntry), 1));
    if (sitInfo->segEntries == nullptr) {
        HMFS_ERROR("Error: Calloc failed for build_sit_info!");
        return -ENOMEM;
    }

    uint32_t bitmapSize = mainSegments * SIT_VBLOCK_MAP_SIZE;
    if (IsSyncDataRecord()) {
        bitmapSize += bitmapSize;
    }
    sitInfo->bitmap = static_cast<unsigned char *>(calloc(bitmapSize, 1));
    if (sitInfo->bitmap == nullptr) {
        HMFS_ERROR("Error: Calloc failed for build_sit_info!!");
        free(sitInfo->segEntries);
        return -ENOMEM;
    }

    unsigned char *bitmap = sitInfo->bitmap;
    for (uint32_t start = 0; start < mainSegments; start++) {
        sitInfo->segEntries[start].currentValidBitmap = bitmap;
        bitmap += SIT_VBLOCK_MAP_SIZE;
        if (IsSyncDataRecord()) {
            sitInfo->segEntries[start].checkpointValidBitmap = bitmap;
            bitmap += SIT_VBLOCK_MAP_SIZE;
        }
    }
    return SUCCESS_CODE;
}

int32_t LoadSit::BuildSitInfo()
{
    SuperBlockData *superBlockData = superBlockInfo_->rawSuper;
    CheckPointData *checkPointData = superBlockInfo_->checkPointData;
    SitInfo *sitInfo = static_cast<SitInfo *>(malloc(sizeof(SitInfo)));
    if (sitInfo == nullptr) {
        HMFS_ERROR("Error: Malloc failed for build_sit_info!");
        return -ENOMEM;
    }

    superBlockInfo_->segmentTable->sitInfo = sitInfo;
    uint32_t mainSegments = superBlockInfo_->segmentTable->mainSegments;
    if (InitializeSitInfo(sitInfo, mainSegments) != SUCCESS_CODE) {
        free(sitInfo);
        return -ENOMEM;
    }

    uint32_t sitSegments = GetLeValue(superBlockData->segmentCountInSIT) >> 1;
    uint32_t bitmapSize = LE32ToNative(checkPointData->sitVersionBitmapSize);
    uint8_t *srcBitmap = static_cast<uint8_t *>(load_->GetBitmapPtr(static_cast<uint32_t>(BitmapType::SIT_BITMAP)));
    uint8_t *dstBitmap = static_cast<uint8_t *>(malloc(bitmapSize));
    if (dstBitmap == nullptr) {
        HMFS_ERROR("Error: Malloc failed for build_sit_info!!");
        free(sitInfo->bitmap);
        free(sitInfo->segEntries);
        free(sitInfo);
        return -ENOMEM;
    }

    int retCode = memcpy_s(dstBitmap, bitmapSize, srcBitmap, bitmapSize);
    HMFS_CHECK_ONLY_LOG(retCode == 0, "Failed to copy sit bitmap");

    sitInfo->sitBaseAddr = GetLeValue(superBlockData->sitBlockId);
    sitInfo->sitBlocks = sitSegments << superBlockInfo_->logBlksPerSeg;
    sitInfo->writtenValidBlocks = GetLeValue(checkPointData->validBlockCount);
    sitInfo->sitBitmap = dstBitmap;
    sitInfo->bitmapSize = bitmapSize;
    sitInfo->dirtySentries = 0;
    sitInfo->sentsPerBlock = SIT_ENTRIES_PER_BLOCK;
    sitInfo->mountElapsedTime = GetLeValue(checkPointData->mountElapsedTime);
    return SUCCESS_CODE;
}

uint32_t LoadSit::GetStartSumBlock()
{
    uint32_t summaryStartBlock = superBlockInfo_->checkPointData->summaryStartBlock;
    return load_->GetStartCpAddr() + LE32ToNative(summaryStartBlock);
}

uint32_t LoadSit::GetSummaryBlockAddr(int32_t base, int32_t type)
{
    uint32_t cpPackTotalBlockCount = superBlockInfo_->checkPointData->checkPointPackBlockCount;
    return load_->GetStartCpAddr() + LE32ToNative(cpPackTotalBlockCount) - (base + 1) + type;
}

void LoadSit::ReadCompactSummaries()
{
    char *kAddr = static_cast<char *>(malloc(HMFS_BLOCK_SIZE));
    HMFS_ASSERT(kAddr);
    uint32_t start = GetStartSumBlock();
    HMFS_CHECK_ONLY_EXPER(HmfsIo::GetInstance().DevReadBlock(kAddr, start++) >= 0, free(kAddr); return);
    CurrentSegmentInfo *currentSegments = reinterpret_cast<CurrentSegmentInfo *>(
        superBlockInfo_->segmentTable->currentSegmentArray + CURSEG_DATA_HOT);
    HMFS_CHECK_ONLY_EXPER(memcpy_s(&currentSegments->summaryBlockData->journal.natCount,
        SUMMARY_JOURNAL_SIZE, kAddr, SUMMARY_JOURNAL_SIZE) == 0, free(kAddr); return);
    currentSegments = reinterpret_cast<CurrentSegmentInfo *>(
        superBlockInfo_->segmentTable->currentSegmentArray + CURSEG_DATA_COLD);
    HMFS_CHECK_ONLY_EXPER(memcpy_s(&currentSegments->summaryBlockData->journal.sitCount, SUMMARY_JOURNAL_SIZE,
        kAddr + SUMMARY_JOURNAL_SIZE, SUMMARY_JOURNAL_SIZE) == 0, free(kAddr); return);
    constexpr uint32_t offsetMultiplier = 2;
    uint32_t offset = offsetMultiplier * SUMMARY_JOURNAL_SIZE;
    for (uint32_t i = CURSEG_DATA_HOT; i <= CURSEG_DATA_COLD; i++) {
        unsigned short blockOffset;
        CurrentSegmentInfo *cursegInfo = static_cast<CurrentSegmentInfo *>(
            superBlockInfo_->segmentTable->currentSegmentArray + i);
        ResetCurSegment(superBlockInfo_, i);
        if (cursegInfo->allocType == static_cast<int32_t>(AllocType::SSR)) {
            blockOffset = superBlockInfo_->blocksPerSeg;
        } else {
            blockOffset = cursegInfo->nextBlockOffset;
        }
        HMFS_ASSERT_FREE(blockOffset <= ENTRY_COUNT_IN_SUM, kAddr);
        for (uint32_t j = 0; j < blockOffset; j++) {
            SummaryEntry *s = reinterpret_cast<SummaryEntry *>(kAddr + offset);
            cursegInfo->summaryBlockData->entries[j] = *s;
            offset += sizeof(SummaryEntry);
            if (offset + sizeof(SummaryEntry) <= HMFS_BLOCK_SIZE - sizeof(SummaryFooter)) {
                continue;
            }
            HMFS_CHECK_ONLY_EXPER(memset_s(kAddr, HMFS_BLOCK_SIZE, 0, HMFS_BLOCK_SIZE) == 0, free(kAddr); return);
            int32_t ret = HmfsIo::GetInstance().DevReadBlock(kAddr, start++);
            HMFS_ASSERT(ret >= 0);
            offset = 0;
        }
    }
    free(kAddr);
}

void LoadSit::RestoreNodeSummary(uint32_t sitNumber, SummaryBlockData *summaryBlock)
{
    HMFS_CHECK_ONLY_EXPER(summaryBlock != nullptr, return);
    NodeData *nodeBlock = static_cast<NodeData *>(malloc(HMFS_BLOCK_SIZE));
    HMFS_ASSERT(nodeBlock);
    uint32_t addr = superBlockInfo_->segmentTable->mainBlockAddr + ((sitNumber) << superBlockInfo_->logBlksPerSeg);
    SummaryEntry *sumEntry = &summaryBlock->entries[0];
    for (uint32_t i = 0; i < superBlockInfo_->blocksPerSeg; i++, sumEntry++) {
        int32_t ret = HmfsIo::GetInstance().DevReadBlock(nodeBlock, addr);
        HMFS_ASSERT(ret >= 0);
        sumEntry->nodeId = nodeBlock->footer.nodeId;
        addr++;
    }
    free(nodeBlock);
}

void LoadSit::ReadNormalSummaries(int32_t type)
{
    CheckPointData *checkPointData = superBlockInfo_->checkPointData;
    uint32_t sitNumber = 0;
    uint32_t blockAddr = 0;
    if (IsDataSegement(type)) {
        sitNumber = GetLeValue(checkPointData->curDataSegNo[type]);
        if (IsCheckpointFlags(checkPointData, CP_FLAG_UMOUNT)) {
            blockAddr = GetSummaryBlockAddr(CURSEG_TYPE_MAX, type);
        } else {
            blockAddr = GetSummaryBlockAddr(CURSEG_DATA_TYPE_COUNT, type);
        }
    } else {
        sitNumber = GetLeValue(checkPointData->curNodeSegNo[type - CURSEG_NODE_HOT]);
        if (IsCheckpointFlags(checkPointData, CP_FLAG_UMOUNT)) {
            blockAddr = GetSummaryBlockAddr(CURSEG_NODE_TYPE_COUNT, type - CURSEG_NODE_HOT);
        } else {
            blockAddr = superBlockInfo_->segmentTable->ssaBlockAddr + sitNumber;
        }
    }
    SummaryBlockData *sumBlk = static_cast<SummaryBlockData *>(malloc(sizeof(*sumBlk)));
    HMFS_ASSERT(sumBlk);
    int32_t ret = HmfsIo::GetInstance().DevReadBlock(sumBlk, blockAddr);
    HMFS_ASSERT_FREE(ret >= 0, sumBlk);

    if (IsNodeSegement(type) && !IsCheckpointFlags(checkPointData, CP_FLAG_UMOUNT)) {
        RestoreNodeSummary(sitNumber, sumBlk);
    }
    CurrentSegmentInfo *currentSegments = GetCurrentSegment(superBlockInfo_, type);
    int retCode = memcpy_s(currentSegments->summaryBlockData, sizeof(*sumBlk), sumBlk, sizeof(*sumBlk));
    free(sumBlk);
    HMFS_CHECK_ONLY_LOG(retCode == 0, "Failed to copy summaryBlockData");
    ResetCurSegment(superBlockInfo_, type);
}

void LoadSit::UpdateCurSegSummaries()
{
    int type = CURSEG_DATA_HOT;

    if (IsCheckpointFlags(superBlockInfo_->checkPointData, CP_FLAG_COMPACT_SUM)) {
        ReadCompactSummaries();
        type = CURSEG_NODE_HOT;
    }

    for (; type <= CURSEG_NODE_COLD; type++) {
        ReadNormalSummaries(type);
    }
}

int32_t LoadSit::BuildCurSegment()
{
    CheckPointData *checkPointData = superBlockInfo_->checkPointData;
    unsigned short blockOffset = 0;
    uint32_t segmentNumber = 0;
    CurrentSegmentInfo *array = static_cast<CurrentSegmentInfo *>(malloc(sizeof(*array) * CURSEG_TYPE_MAX));
    if (array == nullptr) {
        HMFS_ERROR("Error: Malloc failed for build_curseg!");
        return -ENOMEM;
    }
    superBlockInfo_->segmentTable->currentSegmentArray = array;
    for (int32_t i = 0; i < CURSEG_TYPE_MAX; i++) {
        array[i].summaryBlockData = static_cast<SummaryBlockData *>(calloc(sizeof(*(array[i].summaryBlockData)), 1));
        if (array[i].summaryBlockData == nullptr) {
            HMFS_ERROR("Error: Calloc failed for build_curseg!!");
            for (--i; i >= 0; --i) {
                free(array[i].summaryBlockData);
            }
            free(array);
            return -ENOMEM;
        }

        if (i <= CURSEG_DATA_COLD) {
            blockOffset = GetLeValue(checkPointData->curDataBlkOffset[i]);
            segmentNumber = GetLeValue(checkPointData->curDataSegNo[i]);
        }
        if (i > CURSEG_DATA_COLD) {
            blockOffset = GetLeValue(checkPointData->curNodeBlkOffset[i - CURSEG_NODE_HOT]);
            segmentNumber = GetLeValue(checkPointData->curNodeSegNo[i - CURSEG_NODE_HOT]);
        }
        HMFS_ASSERT(segmentNumber < superBlockInfo_->segmentTable->mainSegments);
        HMFS_ASSERT(blockOffset < BLOCKS_PER_SEGMENT);

        array[i].segmentNumber = segmentNumber;
        array[i].zone = (segmentNumber / superBlockInfo_->segmentsPerSection) / superBlockInfo_->sectionsPerZone;
        array[i].nextSegmentNumber = NULL_SEGNO;
        array[i].nextBlockOffset = blockOffset;
        array[i].allocType = checkPointData->allocType[i];
    }
    UpdateCurSegSummaries();
    return SUCCESS_CODE;
}

int32_t LoadSit::BuildSegmentBegin()
{
    HMFS_DEBUG("Enter BuildSegmentBegin");
    SuperBlockData *superBlockData = superBlockInfo_->rawSuper;
    CheckPointData *checkPointData = superBlockInfo_->checkPointData;
    SegmentInfoTable *segmentTable = static_cast<SegmentInfoTable *>(malloc(sizeof(SegmentInfoTable)));
    if (segmentTable == nullptr) {
        HMFS_ERROR("Error: Malloc failed for build_segment_manager!");
        return -ENOMEM;
    }

    superBlockInfo_->segmentTable = segmentTable;
    segmentTable->segmentStartBlockAddr = GetLeValue(superBlockData->segment0BlockId);
    segmentTable->mainBlockAddr = GetLeValue(superBlockData->mainBlockId);
    segmentTable->segmentCount = GetLeValue(superBlockData->segmentCount);
    segmentTable->reservedSegments = GetLeValue(checkPointData->reservedSegmentCount);
    segmentTable->overprovisionedSegments = GetLeValue(checkPointData->overprovisionSegmentCount);
    segmentTable->mainSegments = GetLeValue(superBlockData->segmentCountInMain);
    segmentTable->ssaBlockAddr = GetLeValue(superBlockData->ssaBlockId);

    if (BuildSitInfo() != SUCCESS_CODE || BuildCurSegment() != SUCCESS_CODE) {
        free(segmentTable);
        return -ENOMEM;
    }
    return 0;
}

int32_t LoadSit::BuildSitEntries()
{
    SitInfo *sitInfo = superBlockInfo_->segmentTable->sitInfo;
    CurrentSegmentInfo *currentSegment = GetCurrentSegment(superBlockInfo_, CURSEG_DATA_COLD);
    JournalEntry *journal = &currentSegment->summaryBlockData->journal;
    SegmentEntry *sitEntry = nullptr;
    SitEntry sit = {};
    int sitBlkCnt = SIT_BLK_CNT(superBlockInfo_);
    uint32_t sitNumber = 0;
    uint32_t startBlk = 0;
    SitBlockData *sitBlk = static_cast<SitBlockData *>(calloc(BLOCK_SZ, 1));
    HMFS_ASSERT(sitBlk);
    do {
        uint32_t readed = load_->HmfsRaMetaPages(startBlk, MAX_RA_BLOCKS, static_cast<int32_t>(MetaType::META_SIT));
        sitNumber = startBlk * sitInfo->sentsPerBlock;
        uint32_t end = (startBlk + readed) * sitInfo->sentsPerBlock;
        for (; sitNumber < end && sitNumber < MAIN_SEGS(superBlockInfo_); sitNumber++) {
            sitEntry = &sitInfo->segEntries[sitNumber];
            GetCurrentSitPage(superBlockInfo_, sitNumber, sitBlk);
            sit = sitBlk->entries[SIT_ENTRY_OFFSET(sitInfo, sitNumber)];
            CheckBlockCount(sitNumber, &sit);
            SegInfoFromRawSit(sitEntry, &sit);
        }
        startBlk += readed;
    } while (startBlk < sitBlkCnt);
    free(sitBlk);
    if (SITS_IN_CURSUM(journal) > SIT_JOURNAL_ENTRY_COUNT) {
        HMFS_ERROR("build_sit_entries truncate sitCount(%u) to SIT_JOURNAL_ENTRY_COUNT(%zu)",
            SITS_IN_CURSUM(journal), SIT_JOURNAL_ENTRY_COUNT);
        journal->sitCount = NativeToLE16(SIT_JOURNAL_ENTRY_COUNT);
        config_->fixOn = 1;
    }

    for (uint32_t i = 0; i < SITS_IN_CURSUM(journal); i++) {
        sitNumber = LE32ToNative(SEGNO_IN_JOURNAL(journal, i));
        if (sitNumber >= MAIN_SEGS(superBlockInfo_)) {
            HMFS_ERROR("build_sit_entries: sitNumber(%u) is invalid!!!", sitNumber);
            journal->sitCount = NativeToLE16(i);
            config_->fixOn = 1;
            continue;
        }
        sitEntry = &sitInfo->segEntries[sitNumber];
        sit = SIT_IN_JOURNAL(journal, i);
        CheckBlockCount(sitNumber, &sit);
        SegInfoFromRawSit(sitEntry, &sit);
    }
    return SUCCESS_CODE;
}

void LoadSit::InsideSegInfoFromRawSit(SegmentEntry *sitEntry, SitEntry *rawSit)
{
    HMFS_CHECK_ONLY_EXPER(sitEntry != nullptr && rawSit != nullptr, return);
    sitEntry->validBlocks = GetSitVblocks(rawSit);
    int ret = memcpy_s(sitEntry->currentValidBitmap, SIT_VBLOCK_MAP_SIZE, rawSit->blockBitmap, SIT_VBLOCK_MAP_SIZE);
    HMFS_CHECK_ONLY_LOG(ret == 0, "Failed to copy block bitmap");
    sitEntry->type = GetSitType(rawSit);
    sitEntry->originalType = GetSitType(rawSit);
    sitEntry->modificationTime = LE64ToNative(rawSit->modificationTime);
}

void LoadSit::SegInfoFromRawSit(SegmentEntry *sitEntry, SitEntry *rawSit)
{
    HMFS_CHECK_ONLY_EXPER(sitEntry != nullptr && rawSit != nullptr, return);
    InsideSegInfoFromRawSit(sitEntry, rawSit);
    if (!NeedFsyncDataRecord(superBlockInfo_, config_)) {
        return;
    }
    sitEntry->checkPointValidBlocks = sitEntry->validBlocks;
    int ret = memcpy_s(sitEntry->checkpointValidBitmap, SIT_VBLOCK_MAP_SIZE,
        sitEntry->currentValidBitmap, SIT_VBLOCK_MAP_SIZE);
    HMFS_CHECK_ONLY_LOG(ret == 0, "Failed to copy valid bitmap");
    sitEntry->checkPointType = sitEntry->type;
}

void LoadSit::CheckBlockCount(uint32_t sitNumber, SitEntry *rawSit)
{
    HMFS_CHECK_ONLY_EXPER(rawSit != nullptr, return);
    SegmentInfoTable *smInfo = superBlockInfo_->segmentTable;
    uint32_t end_segno = smInfo->segmentCount - 1;
    int validBlocks = 0;

    if (GetSitVblocks(rawSit) > superBlockInfo_->blocksPerSeg) {
        HMFS_ERROR("Invalid SIT vblocks: sitNumber=0x%x, %u", sitNumber, GetSitVblocks(rawSit));
    }

    if (sitNumber > end_segno) {
        HMFS_ERROR("Invalid SEGNO: 0x%x", sitNumber);
    }

    for (uint32_t i = 0; i < SIT_VBLOCK_MAP_SIZE; i++) {
        validBlocks += HmfsCommon::GetInstance().GetBitsInByte(rawSit->blockBitmap[i]);
    }
    if (GetSitVblocks(rawSit) != validBlocks) {
        HMFS_ERROR("Wrong SIT valid blocks: sitNumber=0x%x, %u vs. %u", sitNumber, GetSitVblocks(rawSit), validBlocks);
    }
    if (GetSitType(rawSit) >= CURSEG_TYPE_MAX) {
        HMFS_ERROR("Wrong SIT type: sitNumber=0x%x, %u", sitNumber, GetSitType(rawSit));
    }
}

int32_t LoadSit::LateBuildSegmentManager()
{
    HMFS_DEBUG("Enter LateBuildSegmentManager");
    if (superBlockInfo_->segManagerDone) {
        return EXIT_ERR_CODE;
    }
    superBlockInfo_->segManagerDone = true;
    if (BuildSitEntries()) {
        free(superBlockInfo_->segmentTable);
        return -ENOMEM;
    }
    return SUCCESS_CODE;
}
} // namespace Hmfs
} // namespace OHOS